import serial
import time
import os
import threading
import hashlib
from urllib.parse import urlparse, parse_qs

class SerialReader(threading.Thread):
    def __init__(self, ser, output_file):
        super(SerialReader, self).__init__()
        self.ser = ser
        self.com_port = 'COM3'
        self.baud_rate = 115200
        self.output_file = output_file
        self.stop_event = threading.Event()
        self.lock = threading.Lock()
        self.condition = threading.Condition(self.lock)
        self.response_ready = False  # 响应读取标志
        # QHTTPGETEX指令参数
        self.total_size = 202317  # 数据总大小
        self.current_position = 0  # 当前读取位置
        self.read_len = 1024  # 每次读取的长度
        # 升级相关参数
        self.task_id = 181537  # 任务ID
        self.device_id = 170856841234567  # 设备ID
        self.access_token = 'JnBHGnQsFG'  # 访问令牌
        self.url = f'https://iot.ctwing.cn/dmfile/file/mqtt/upgrade?fileId=2fb1f7fc142145cd996e2014313d357e&taskId={self.task_id}&deviceId={self.device_id}&accessToken={self.access_token}'
        self.version = '1.0.0'  # 固件版本
        # 校验相关参数
        self.file_id = None  # 新增变量保存 fileId
        self.received_data_md5 = None  # 保存接收到数据的 MD5 签名

    def run(self):
        with open(self.output_file, 'ab') as f:
            count=0       
            while not self.stop_event.is_set():
                with self.condition:  # 使用条件变量锁
                    data = self.ser.read(1200)  # 每次读取1200字节              
                    if data:
                        if self.is_binary_data(data):
                            # 截去开头的字符串数据（以 CONNECT\r\n 截止）
                            if b'CONNECT\r\n' in data:
                                data = data.split(b'CONNECT\r\n', 1)[1]
                            # 截去结尾的字符串数据（以 \r\nOK\r\n 开始）
                            if b'\r\nOK\r\n' in data:
                                data = data.split(b'\r\nOK\r\n', 1)[0]
                            f.write(data)
                            count+=1
                            print(f"接收到二进制数据:{data}")
                            print(f"接收到第{count}个数据块，当前位置：{self.current_position}")
                        else:
                            # 将 \r\n 转换成实际的回车换行符
                            response_str = data.decode().replace('\r\n', '\n')
                            print(f"接收到响应数据:\n{response_str}")
                    self.response_ready = True
                    self.condition.notify()  # 通知写操作可以继续
                    
                time.sleep(0.1)  # 等待0.1秒，避免过度占用CPU资源
            
            if self.current_position >= self.total_size:
                # 计算文件数据的 MD5
                f.seek(0)  # 重置指针到文件开头
                file_data = f.read()  # 读取所有数据
                self.received_data_md5 = hashlib.md5(file_data).hexdigest()  # 计算 MD5
                print(f"接收到数据的 MD5 签名: {self.received_data_md5}")
                # 比较 fileId
                if self.file_id:
                    print(f"与提取的 fileId 进行比对: {self.file_id}")
            print(f"数据接收完成，文件保存为 {self.output_file}")

    def stop(self):
        self.stop_event.set()

    def send_data(self, data):
        with self.condition:  # 使用条件变量
            """向串口发送数据并读取响应"""
            if self.ser.is_open:
                # 在数据结尾添加回车换行符
                data_with_crlf = data + b'\r\n'
                 # 处理 AT+QHTTPURL 指令
                if data.startswith(b'AT+QHTTPURL'):
                    url_input = input("请输入 URL: ")
                    parsed_url = urlparse(url_input)
                    query_params = parse_qs(parsed_url.query)
                    if 'fileId' in query_params:
                        self.file_id = query_params['fileId'][0]  # 提取 fileId
                        print(f"提取到的 fileId: {self.file_id}")
                # 如果是 AT+QHTTPGETEX 指令，自动分段读取数据
                if data.startswith(b'AT+QHTTPGETEX'):
                    # 需要发送的AT
                    # ATE0
                    # AT+QHTTPURL=149,80
                    # https://iot.ctwing.cn/dmfile/file/mqtt/upgrade?fileId=8232cf24f6604e008bcf804e42912d62&taskId=182679&deviceId=170856841234567&accessToken=cBuztENkys
                    # AT+QHTTPGETEX
                    while self.current_position < self.total_size:
                        # 计算下一段数据的起始位置和长度
                        next_position = self.current_position + self.read_len
                        if next_position > self.total_size:
                            next_position = self.total_size
                        read_len = next_position - self.current_position

                        # 发送分段GET指令
                        self.condition.wait_for(lambda: self.response_ready)
                        self.response_ready = False
                        next_command = f'AT+QHTTPGETEX=80,{self.current_position},{read_len}\r\n'.encode()
                        self.ser.write(next_command)
                        print(f"已发送GETEX指令: {next_command}")   

                        time.sleep(2)                                       

                        self.condition.wait_for(lambda: self.response_ready)
                        self.response_ready = False
                        # 发送 AT+QHTTPREAD=80 指令
                        read_command = b'AT+QHTTPREAD=80\r\n'
                        self.ser.write(read_command)
                        print(f"已发送读指令: {read_command}")
                        

                        # 更新当前读取位置
                        self.current_position = next_position
                    # 分段GET结束后，上报升级进度
                    progress_report = b'AT+CTMQPUB="/ota/progress",0,0,"{"taskId":' + self.task_id + ',"params":{"step":100,"desc":"Done","module":"MCU"}}"\r\n'
                    self.ser.write(progress_report)
                    print(f"已发送升级进度报告: {progress_report}")

                    time.sleep(1)  

                    # 上报升级后固件版本
                    firmware_version_report = b'AT+CTMQPUB="/ota/inform",0,0,"{"taskId":' + self.task_id + ',"params":{"version":"' + self.version + '","module":"MCU"}}"\r\n'
                    self.ser.write(firmware_version_report)
                    print(f"已发送固件版本报告: {firmware_version_report}")
                        
                else:
                    self.condition.wait_for(lambda: self.response_ready)
                    self.response_ready = False
                    # 发送普通指令
                    self.ser.write(data_with_crlf)
    

    def is_binary_data(self, data):
        """判断数据是否为二进制数据"""
        try:
            data.decode('utf-8')
            return False
        except UnicodeDecodeError:
            return True

def open_serial_port(port, baudrate):
    """打开串口并返回串口对象"""
    try:
        ser = serial.Serial(port, baudrate, timeout=1)
        print(f"成功打开串口 {port}，波特率: {baudrate}")
        return ser
    except serial.SerialException as e:
        print(f"无法打开串口 {port}: {e}")
        return None

def main():
    file_name = 'test.bin'
    # 获取当前代码文件所在的目录
    current_file_path = os.path.dirname(os.path.abspath(__file__))
    # 拼接文件路径
    output_file = os.path.join(current_file_path, file_name)

    ser = open_serial_port('COM3', 115200)
    if ser:
        reader = SerialReader(ser, output_file)
        reader.start()
        try:
            while True:
                # 示例：向串口发送数据
                send_data = input("请输入要发送的数据（输入 'exit' 退出）: ")
                if send_data.lower() == 'exit':
                    break
                reader.send_data(send_data.encode())
                time.sleep(1)  # 等待1秒
        except KeyboardInterrupt:
            pass
        finally:
            reader.stop()
            reader.join()
            ser.close()
            print("串口已关闭")

if __name__ == "__main__":
    main()
